package cn.yanceysong.scene.s1_s10.设计朋友圈点赞;

import lombok.AllArgsConstructor;
import org.redisson.api.RScoredSortedSet;
import org.redisson.api.RedissonClient;
import org.redisson.client.protocol.ScoredEntry;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;

public class RedisLikeDemo {
    /**
     * .场景题:设计朋友圈点赞功能
     * .
     * .需求分析:
     * .1. 记录某个朋友圈的点赞数量
     * .2. 支持点赞和取消点赞操作
     * .3. 支持查看点赞列表(哪些人点过赞)
     * .4. 点赞列表需要按照时间顺序展示(先点赞的在前)
     * .5. 同一用户对同一朋友圈只能点赞一次(去重)
     * .
     * .思考方式:
     * .1. 首先考虑需要存储的信息:
     * .   - 朋友圈ID(用于区分不同的朋友圈)
     * .   - 点赞用户ID(记录谁点了赞)
     * .   - 点赞时间(用于排序显示)
     * .
     * .2. 考虑需要支持的操作:
     * .   - 点赞:添加用户ID到点赞列表
     * .   - 取消点赞:从点赞列表移除用户ID
     * .   - 查询点赞列表:按时间顺序获取所有点赞用户
     * .   - 查询点赞数量:统计点赞人数
     * .   - 检查是否点赞:判断某用户是否已点赞
     * .
     * .3. 数据结构选择:
     * .   为什么选择Redis的ZSet(有序集合)?
     * .   - 自动去重:用户ID作为member,天然去重
     * .   - 时间排序:点赞时间戳作为score,自动按时间排序
     * .   - 高效查询:支持范围查询、计数、成员检查等O(logN)操作
     * .   - 简单删除:通过用户ID直接删除,O(logN)时间复杂度
     * .
     * .解题思路:
     * .1. 使用Redis的ZSet(Sorted Set)存储点赞信息
     * .   - Key: "like:" + 朋友圈ID (如: "like:post123")
     * .   - Member: 用户ID (如: "user456")
     * .   - Score: 点赞时间戳 (如: 1700000000000L)
     * .
     * .2. 数据结构示意图:
     * .   Redis Key: "like:post123"
     * .   +------------------+-------+
     * .   |     Member       | Score |
     * .   +------------------+-------+
     * .   | "user001"        | 1001  |  <- 最早点赞
     * .   | "user002"        | 1002  |
     * .   | "user003"        | 1003  |
     * .   | "user004"        | 1004  |  <- 最晚点赞
     * .   +------------------+-------+
     * .
     * .3. 核心操作实现:
     * .   - 点赞: ZADD like:postId timestamp userId
     * .   - 取消点赞: ZREM like:postId userId
     * .   - 获取点赞列表: ZRANGE like:postId 0 -1 (按时间升序)
     * .   - 获取点赞数: ZCARD like:postId
     * .   - 检查是否点赞: ZSCORE like:postId userId != null
     * .
     * .实现的数据结构:
     * .- Redis ZSet (Sorted Set / 有序集合)
     * .  底层实现:跳表(Skip List) + 哈希表(Hash Table)
     * .  - 跳表保证有序性,支持范围查询
     * .  - 哈希表保证O(1)的成员查找
     * .
     * .时间复杂度:
     * .- 点赞/取消点赞: O(logN) - N为点赞人数
     * .- 查询点赞列表: O(M+logN) - M为返回元素数量
     * .- 查询点赞数: O(1)
     * .- 检查是否点赞: O(1)
     * .
     * .空间复杂度: O(N) - N为点赞人数
     */
    @AllArgsConstructor
    public static class LikeService {
        private final RedissonClient redissonClient;

        private static final String LIKE_PREFIX = "like:";

        /**
         * .点赞操作
         * .
         * .操作说明:
         * .1. 构造Redis Key: "like:" + postId
         * .2. 获取ZSet对象
         * .3. 添加成员(userId)和分数(当前时间戳)
         * .
         * .注意事项:
         * .- 如果用户已经点过赞,会更新时间戳(但实际业务中通常不允许重复点赞)
         * .- 使用当前时间戳作为score,保证先点赞的排在前面
         * .
         * .@param postId 朋友圈ID
         * .@param userId 用户ID
         * .@return true:点赞成功(新增), false:已经点过赞(更新时间)
         */
        public boolean likePost(String postId, String userId) {
            String key = LIKE_PREFIX + postId;
            RScoredSortedSet<String> sortedSet = redissonClient.getScoredSortedSet(key);

            // 检查是否已经点过赞
            if (sortedSet.contains(userId)) {
                return false;  // 已经点过赞,不允许重复点赞
            }

            // 添加点赞记录,使用当前时间戳作为分数
            long timestamp = System.currentTimeMillis();
            sortedSet.add(timestamp, userId);
            return true;
        }

        /**
         * .取消点赞操作
         * .
         * .操作说明:
         * .1. 构造Redis Key
         * .2. 获取ZSet对象
         * .3. 移除指定成员(userId)
         * .
         * .@param postId 朋友圈ID
         * .@param userId 用户ID
         * .@return true:取消成功, false:本来就没点过赞
         */
        public boolean unlikePost(String postId, String userId) {
            String key = LIKE_PREFIX + postId;
            RScoredSortedSet<String> sortedSet = redissonClient.getScoredSortedSet(key);

            // 移除点赞记录,返回是否移除成功
            return sortedSet.remove(userId);
        }

        /**
         * .获取点赞列表(按时间顺序)
         * .
         * .操作说明:
         * .1. 使用ZRANGE获取所有成员(0到-1表示全部)
         * .2. 默认按score升序排列,即按点赞时间从早到晚
         * .
         * .返回示例:
         * .["user001", "user002", "user003"]  <- 最前面的是最早点赞的
         * .
         * .@param postId 朋友圈ID
         * .@return 点赞用户ID列表,按点赞时间升序
         */
        public List<String> getLikeList(String postId) {
            String key = LIKE_PREFIX + postId;
            RScoredSortedSet<String> sortedSet = redissonClient.getScoredSortedSet(key);

            // 获取全部成员,按score升序(时间从早到晚)
            Collection<String> users = sortedSet.readAll();
            return new ArrayList<>(users);
        }

        /**
         * .获取点赞列表(带时间戳)
         * .
         * .用于需要显示点赞时间的场景
         * .
         * .@param postId 朋友圈ID
         * .@return 点赞用户ID和时间戳列表
         */
        public List<LikeInfo> getLikeListWithTime(String postId) {
            String key = LIKE_PREFIX + postId;
            RScoredSortedSet<String> sortedSet = redissonClient.getScoredSortedSet(key);

            // 获取全部成员及其分数
            Collection<ScoredEntry<String>> entries = sortedSet.entryRange(0, -1);
            return entries.stream()
                    .map(entry -> new LikeInfo(entry.getValue(), entry.getScore().longValue()))
                    .collect(Collectors.toList());
        }

        /**
         * .获取点赞数量
         * .
         * .使用ZCARD命令,时间复杂度O(1)
         * .
         * .@param postId 朋友圈ID
         * .@return 点赞数量
         */
        public int getLikeCount(String postId) {
            String key = LIKE_PREFIX + postId;
            RScoredSortedSet<String> sortedSet = redissonClient.getScoredSortedSet(key);
            return sortedSet.size();
        }

        /**
         * .检查用户是否点过赞
         * .
         * .@param postId 朋友圈ID
         * .@param userId 用户ID
         * .@return true:已点赞, false:未点赞
         */
        public boolean hasLiked(String postId, String userId) {
            String key = LIKE_PREFIX + postId;
            RScoredSortedSet<String> sortedSet = redissonClient.getScoredSortedSet(key);
            return sortedSet.contains(userId);
        }

        /**
         * .获取最早点赞的N个用户
         * .
         * .用于"XXX等N人点赞"的场景
         * .
         * .@param postId 朋友圈ID
         * .@param limit 限制数量
         * .@return 最早点赞的用户列表
         */
        public List<String> getTopNLikers(String postId, int limit) {
            String key = LIKE_PREFIX + postId;
            RScoredSortedSet<String> sortedSet = redissonClient.getScoredSortedSet(key);

            // 获取前N个成员(按score升序)
            Collection<String> users = sortedSet.readAll();
            return users.stream()
                    .limit(limit)
                    .collect(Collectors.toList());
        }

        /**
         * .点赞信息封装类
         */
        public static class LikeInfo {
            private final String userId;
            private final long timestamp;

            public LikeInfo(String userId, long timestamp) {
                this.userId = userId;
                this.timestamp = timestamp;
            }

            public String getUserId() {
                return userId;
            }

            public long getTimestamp() {
                return timestamp;
            }

            @Override
            public String toString() {
                return "LikeInfo{userId='" + userId + "', timestamp=" + timestamp + "}";
            }
        }
    }

    public static void main(String[] args) {
        System.out.println("=== 朋友圈点赞功能测试 ===");
        System.out.println();
        System.out.println("注意:本测试需要配置RedissonClient才能运行");
        System.out.println("测试场景说明:");
        System.out.println("1. 基本点赞功能测试");
        System.out.println("2. 重复点赞测试(不允许重复点赞)");
        System.out.println("3. 取消点赞测试");
        System.out.println("4. 点赞列表顺序测试(按时间升序)");
        System.out.println("5. 点赞数量统计测试");
        System.out.println("6. 检查点赞状态测试");
        System.out.println("7. 获取Top N点赞用户测试");
        System.out.println();

        // 实际运行需要注入RedissonClient
        // RedissonClient redissonClient = ...; // 从Spring容器获取
        // LikeService likeService = solution.new LikeService(redissonClient);
        // testLikeService(likeService);
    }

    /**
     * .测试点赞服务
     * .
     * .需要在Spring环境中运行,确保RedissonClient已配置
     */
    private static void testLikeService(LikeService likeService) {
        String postId = "post_001";

        System.out.println("--- 测试1: 基本点赞功能 ---");
        boolean result1 = likeService.likePost(postId, "user_001");
        System.out.println("user_001点赞: " + result1 + " (期望: true)");

        boolean result2 = likeService.likePost(postId, "user_002");
        System.out.println("user_002点赞: " + result2 + " (期望: true)");

        System.out.println("\n--- 测试2: 重复点赞测试 ---");
        boolean result3 = likeService.likePost(postId, "user_001");
        System.out.println("user_001再次点赞: " + result3 + " (期望: false, 不允许重复点赞)");

        System.out.println("\n--- 测试3: 查询点赞数量 ---");
        int count = likeService.getLikeCount(postId);
        System.out.println("点赞数量: " + count + " (期望: 2)");

        System.out.println("\n--- 测试4: 查询点赞列表 ---");
        List<String> likeList = likeService.getLikeList(postId);
        System.out.println("点赞列表: " + likeList + " (期望: [user_001, user_002])");

        System.out.println("\n--- 测试5: 检查点赞状态 ---");
        boolean hasLiked1 = likeService.hasLiked(postId, "user_001");
        System.out.println("user_001是否点赞: " + hasLiked1 + " (期望: true)");

        boolean hasLiked2 = likeService.hasLiked(postId, "user_003");
        System.out.println("user_003是否点赞: " + hasLiked2 + " (期望: false)");

        System.out.println("\n--- 测试6: 取消点赞 ---");
        boolean unlike1 = likeService.unlikePost(postId, "user_001");
        System.out.println("user_001取消点赞: " + unlike1 + " (期望: true)");

        int countAfter = likeService.getLikeCount(postId);
        System.out.println("取消后点赞数量: " + countAfter + " (期望: 1)");

        System.out.println("\n--- 测试7: 获取Top N点赞用户 ---");
        likeService.likePost(postId, "user_003");
        likeService.likePost(postId, "user_004");
        likeService.likePost(postId, "user_005");

        List<String> top3 = likeService.getTopNLikers(postId, 3);
        System.out.println("最早点赞的3个用户: " + top3);

        System.out.println("\n=== 所有测试完成 ===");
    }
}
